// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: discovery support client and server
// ------------------------------------------------------------------

#ifndef _EDDS_ENTITYSERVERATTRIBUTES_H_
#define _EDDS_ENTITYSERVERATTRIBUTES_H_
#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC

#include <edds/rtps/common/Guid.h>
#include <ertps/utils/LocatorAlias.h>

#include <algorithm>
#include <iterator>
#include <list>

namespace evbs {

namespace ertps {
namespace rtps {
class ParticipantProxyData;
}  // namespace rtps
}  // namespace ertps

namespace edds {
namespace rtps {

/**
 * Class RemoteServerAttributes, to define the attributes of the Discovery Server Protocol.
 * @ingroup RTPS_ATTRIBUTES_MODULE
 */

class RemoteServerAttributes {
 public:
    RTPS_DllAPI inline bool operator==(const RemoteServerAttributes& r) const {
        return guidPrefix == r.guidPrefix && metatrafficUnicastLocatorList == r.metatrafficUnicastLocatorList &&
               metatrafficMulticastLocatorList == r.metatrafficMulticastLocatorList;
        //     && proxy == r.proxy;
    }

    RTPS_DllAPI void clear() {
        guidPrefix = ertps::rtps::GuidPrefix_t::unknown();
        metatrafficUnicastLocatorList.clear();
        metatrafficMulticastLocatorList.clear();
        proxy = nullptr;
    }

    RTPS_DllAPI ertps::rtps::GUID_t GetParticipant() const;

    RTPS_DllAPI ertps::rtps::GUID_t GetPDPReader() const;
    RTPS_DllAPI ertps::rtps::GUID_t GetPDPWriter() const;

    RTPS_DllAPI inline bool ReadguidPrefix(const char* pfx) { return bool(std::istringstream(pfx) >> guidPrefix); }

    //! Metatraffic Unicast Locator List
    LocatorList metatrafficUnicastLocatorList;
    //! Metatraffic Multicast Locator List.
    LocatorList metatrafficMulticastLocatorList;

    //! Guid prefix
    ertps::rtps::GuidPrefix_t guidPrefix;

    // Live participant proxy reference
    const ertps::rtps::ParticipantProxyData* proxy {};

    // Check if there are specific transport locators associated
    // the template parameter is the locator kind (e.g. LOCATOR_KIND_UDPv4)
    template <int kind>
    bool requires_transport() const {
        return metatrafficUnicastLocatorList.has_kind<kind>() || metatrafficMulticastLocatorList.has_kind<kind>();
    }
};

typedef std::list<RemoteServerAttributes> RemoteServerList_t;

template <class charT>
struct server_ostream_separators {
    static const charT* list_separator;
    static const charT* locator_separator;
};

#ifndef _MSC_VER
template <>
const char* server_ostream_separators<char>::list_separator;
template <>
const wchar_t* server_ostream_separators<wchar_t>::list_separator;

template <>
const char* server_ostream_separators<char>::locator_separator;
template <>
const wchar_t* server_ostream_separators<wchar_t>::locator_separator;
#endif  // _MSC_VER

template <class charT>
std::basic_ostream<charT>& operator<<(std::basic_ostream<charT>& output, const RemoteServerAttributes& sa) {
    typename std::basic_ostream<charT>::sentry s(output);
    output << sa.guidPrefix;
    if (!sa.metatrafficUnicastLocatorList.empty()) {
        output << server_ostream_separators<charT>::locator_separator << sa.metatrafficUnicastLocatorList;
    }
    if (!sa.metatrafficMulticastLocatorList.empty()) {
        output << server_ostream_separators<charT>::locator_separator << sa.metatrafficUnicastLocatorList;
    }
    return output;
}

template <class charT>
std::basic_ostream<charT>& operator<<(std::basic_ostream<charT>& output, const RemoteServerList_t& list) {
    typename std::basic_ostream<charT>::sentry s(output);
    std::ostream_iterator<RemoteServerAttributes> os_iterator(output, server_ostream_separators<charT>::list_separator);
    std::copy(list.begin(), list.end(), os_iterator);
    return output;
}

// port use if the ros environment variable doesn't specified one
constexpr uint16_t DEFAULT_VBS_SERVER_PORT = 11811U;
// Port used by default for tcp transport
constexpr uint16_t DEFAULT_VBS_TCP_SERVER_PORT = 42100U;
// default server base guidPrefix
const char* const DEFAULT_VBS_SERVER_GUIDPREFIX = "44.53.00.5f.45.50.52.4f.53.49.4d.41";

/* Environment variable to specify a semicolon-separated list of UDPv4 locators (ip:port) that
 * define remote server locators. The position in the list is used as a "server id" to extrapolate
 * the server's GUID prefix. For the variable to take any effect, the following pre-conditions must
 * be met:
 *    1. The server's GUID prefix must be compliant with the schema
 *       "44.53.<server_id_in_hex>.5f.45.50.52.4f.53.49.4d.41", which is the schema followed by the
 * prefixes generated when creating server using edds cli, being DEFAULT_VBS_SERVER_GUIDPREFIX the
 * prefix for ID=0.
 *    1. The discovery protocol must be either SIMPLE or SERVER.
 *       1. In the case of SIMPLE, the participant is created as a CLIENT instead.
 *       1. In the case of SERVER, the participant is created as a SERVER, using the
 * DEFAULT_VBS_MASTER_URI list to expand the list of remote servers.
 */
const char* const DEFAULT_VBS_MASTER_URI = "VBS_DISCOVERY_SERVER";

/**
 * Retrieves a semicolon-separated list of locators from a string, and
 * populates a LocatorList_t.
 * @param [in] list servers listening locator list.
 * @param [out] servers_list reference to a LocatorList_t to populate.
 * @return true if parsing succeeds, false otherwise (or if the list is empty)
 */
bool load_environment_server_info(const std::string& list, LocatorList& servers_list);

/**
 * Retrieves a semicolon-separated list of locators from DEFAULT_VBS_MASTER_URI environment variable, and
 * populates a LocatorList_t.
 *
 * The environment variable can be read from an environment file (which allows runtime modification of the remote
 * servers list) or directly from the environment.
 * The value contained in the file takes precedence over the environment value (if both are set).
 *
 * @param [out] servers_list reference to a LocatorList_t to populate.
 * @return true if parsing succeeds, false otherwise
 */
bool load_environment_server_info(LocatorList& servers_list);

/**
 * Get the value of environment variable DEFAULT_VBS_MASTER_URI
 * @return The value of environment variable DEFAULT_VBS_MASTER_URI. Empty string if the variable
 * is not defined.
 */
RTPS_DllAPI const std::string& vbs_discovery_server_env();

/**
 * Returns the guidPrefix associated to the given server id
 * @param[in] id of the default server whose guidPrefix we want to retrieve
 * @param[out] guid reference to the guidPrefix to modify
 * @return true if the server guid can be delivered
 */
// RTPS_DllAPI bool get_server_client_default_guidPrefix(int id, ertps::rtps::GuidPrefix_t& guid);

}  // namespace rtps
}  // namespace edds

// keep former namespace references available
namespace ertps {
namespace rtps {

using edds::rtps::DEFAULT_VBS_MASTER_URI;
using edds::rtps::DEFAULT_VBS_SERVER_GUIDPREFIX;
using edds::rtps::DEFAULT_VBS_SERVER_PORT;
using edds::rtps::DEFAULT_VBS_TCP_SERVER_PORT;
// using edds::rtps::get_server_client_default_guidPrefix;
using edds::rtps::load_environment_server_info;
using edds::rtps::RemoteServerAttributes;
using edds::rtps::RemoteServerList_t;
using edds::rtps::vbs_discovery_server_env;

}  // namespace rtps
}  // namespace ertps

}  // namespace evbs

#endif  // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
#endif  /* _EDDS_ENTITYSERVERATTRIBUTES_H_ */
